﻿using Albatross.WPF.Common.ViewModels;
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;

namespace Albatross.SharePoint.Explorer.ViewModels {
	public class ClientObjectCollectionViewModel : ClientObjectViewModel {
		public ClientObjectCollectionViewModel(ClientContext context, string name, ClientObjectCollection clientObjectCollection) : base(context, name, clientObjectCollection) {}

		#region properties
		public ClientObjectCollection ClientObjectCollection { get { return (ClientObjectCollection)ClientObject; } }
		#endregion

		public override IEnumerable<string> PreloadProperties {
			get { return new string[] { SchemaXml_Property, Hidden_Property }; }
		}

		public const string Hidden_Property = "Hidden";
		public override bool IsHidden {
			get {
				PropertyInfo property = ClientObject.GetType().GetProperty(Hidden_Property);
				if (property == null) {
					return false;
				} else {
					if (ClientObject.IsPropertyAvailable(Hidden_Property)) {
						return (bool)property.GetValue(ClientObject);
					} else {
						return false;
					}
				}
			}
		}

		public virtual Type ItemType {
			get {
				Type type = ClientObjectCollection.GetType();
				Type genericTypeDefinition = typeof(ClientObjectCollection<>);

				while (type != null) {
					if (type.IsGenericType && type.GetGenericTypeDefinition() == genericTypeDefinition) {
						Type[] genericTypeArgs = type.GetGenericArguments();
						if (genericTypeArgs != null && genericTypeArgs.Length > 0) {
							return genericTypeArgs[0];
						}
					} else {
						type = type.BaseType;
					}
				}
				return null;
			}
		}

		#region methods
		public override string GetTreeItemTitle() {
			return string.Format("{0} ({1})", Name, ClientObjectCollection.Count);
		}

		void LoadCollection<T>(ClientObjectCollection<T> clientObjectCollection, IEnumerable<string> properties ) where T : ClientObject {
			List<Expression<Func<T, object>>> list = new List<Expression<Func<T, object>>>();
			Type itemType = typeof(T);
			foreach (string propertyName in properties) {
				PropertyInfo property = itemType.GetProperty(propertyName);
				if (property != null) {
					ParameterExpression args = Expression.Parameter(itemType, "args");
					MemberExpression propertyExpression = Expression.Property(args, property);
					UnaryExpression castExpression = Expression.Convert(propertyExpression, typeof(object));
					Expression<Func<T, object>> expression = Expression.Lambda<Func<T, object>>(castExpression, args);
					list.Add(expression);
				}
			}
			if (list.Count > 0) {
				Expression<Func<T, object>>[] expressions = list.ToArray();
				Context.Load(clientObjectCollection, args => args.IncludeWithDefaultProperties(expressions));
			} else {
				Context.Load(clientObjectCollection);
			}
		}

		protected override Tuple<IEnumerable<ObjectProperty>, IEnumerable<ClientObjectViewModel>> Load() {
			Type type = ClientObjectCollection.GetType();

			List<ObjectProperty> properties = new List<ObjectProperty>();
			List<ClientObjectViewModel> children = new List<ClientObjectViewModel>();
			Tuple<IEnumerable<ObjectProperty>, IEnumerable<ClientObjectViewModel>> tuple = new Tuple<IEnumerable<ObjectProperty>, IEnumerable<ClientObjectViewModel>>(properties, children);
			
			MethodInfo method = typeof(ClientObjectCollectionViewModel).GetMethod("LoadCollection", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(ItemType);
			method.Invoke(this, new object[] { ClientObjectCollection, PreloadProperties });
			Context.ExecuteQuery();

			foreach (PropertyInfo property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)) {
				if (property.PropertyType == typeof(string) || property.PropertyType.IsValueType) {
					if (property.Name != SchemaXml_Property) {
						properties.Add(new ObjectProperty().Set(ClientObject, property));
					}
				}
			}
			foreach (ClientObject clientObject in ClientObjectCollection) {
				ClientObjectViewModel vm = ClientObjectFactory.GetViewModel(Context, string.Empty, clientObject.GetType(), clientObject);
				vm.TreeItemTitle = vm.GetTreeItemTitle();
				children.Add(vm);
			}
			children.Sort();
			return tuple;
		}
		#endregion
	}
}
